package com.example.vidget;

import android.content.Context;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.GestureDetector.OnGestureListener;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.ImageView;

//import android.graphics.Bitmap;

public class TouchImageView extends ImageView implements OnGestureListener {

	Matrix matrix;

	// We can be in one of these 3 states
	static final int NONE = 0;
	static final int DRAG = 1;
	static final int ZOOM = 2;
	int mode = NONE;

	// Remember some things for zooming
	PointF last = new PointF();
	PointF start = new PointF();
	float minScale = 1f;
	float maxScale = 3f;
	float[] m;

	private float mLastMotionX, mLastMotionY;
	private boolean isMoved;
	private Runnable mLongPressRunnable; // long event
	private static final int TOUCH_SLOP = 20;
	private boolean hasResLongEvent = false;

	private boolean atLifeSide = false, atRightSide = false;
	private boolean atTopSide = false, atBottomSide = false;

	int viewWidth, viewHeight;
	static final int CLICK = 3;
	float saveScale = 1f;
	/**
	 * true是表示放大
	 */
	boolean isScale = true;
	protected float origWidth, origHeight;
	int oldMeasuredWidth, oldMeasuredHeight;
	ScaleGestureDetector mScaleDetector;
	GestureDetector gestureDetector;

	Context context;
	float oldScale = 1.0f;

	public TouchImageView(Context context) {
		super(context);
		sharedConstructing(context);
	}

	public TouchImageView(Context context, AttributeSet attrs) {
		super(context, attrs);
		sharedConstructing(context);
	}

	/**
	 * 图片放大且不在图片的左右边界时，返回真，否则返回假
	 */
	public boolean isScaleAndNotAtSide() {
		if (saveScale == 1.0f) {
			return false;
		} else {
			if (atLifeSide || atRightSide)
				return false;
			else
				return true;
		}
	}

	@SuppressWarnings("deprecation")
	private void sharedConstructing(Context context) {
		super.setClickable(true);
		this.context = context;
		mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
		gestureDetector = new GestureDetector(this);
		gestureDetector
				.setOnDoubleTapListener(new GestureDetector.OnDoubleTapListener() {

					@Override
					public boolean onSingleTapConfirmed(MotionEvent e) {
						return false;
					}

					@Override
					public boolean onDoubleTapEvent(MotionEvent e) {
						return false;
					}

					@Override
					public boolean onDoubleTap(MotionEvent e) {
						float mScaleFactor = 1.5f;
						if (isScale) {// 放大
							mScaleFactor = 1.5f;
						} else {// 缩小
							mScaleFactor = 0.5f;
						}
						float origScale = saveScale;
						saveScale *= mScaleFactor;
						if (saveScale > maxScale) {
							isScale = false;
							saveScale = maxScale;
							mScaleFactor = maxScale / origScale;
						} else if (saveScale < minScale) {
							saveScale = minScale;
							isScale = true;
							mScaleFactor = minScale / origScale;
						}

						if (origWidth * saveScale <= viewWidth
								|| origHeight * saveScale <= viewHeight) {
							Log.e(TouchImageView.class.getName(),
									"111111111111111111 if");
							matrix.postScale(mScaleFactor, mScaleFactor,
									viewWidth / 2,
									viewHeight / 2);
							fixTransCenter();
						} else {
							matrix.postScale(mScaleFactor, mScaleFactor,
									e.getX(),e.getY());
							Log.e(TouchImageView.class.getName(),
									"111111111111111111 else");
							fixTrans();
						}
						return true;
					}
				});
		matrix = new Matrix();
		m = new float[9];
		setImageMatrix(matrix);
		setScaleType(ScaleType.MATRIX);

		mLongPressRunnable = new Runnable() {
			@Override
			public void run() {
				hasResLongEvent = true;
				performLongClick();
			}
		};

		setOnTouchListener(new OnTouchListener() {

			@Override
			public boolean onTouch(View v, MotionEvent event) {

				mScaleDetector.onTouchEvent(event);
				gestureDetector.onTouchEvent(event);
				PointF curr = new PointF(event.getX(), event.getY());

				switch (event.getAction()) {
				case MotionEvent.ACTION_DOWN:
					last.set(curr);
					start.set(last);
					mode = DRAG;
					atLifeSide = atRightSide = false;

					// add long click event
					mLastMotionX = curr.x;
					mLastMotionY = curr.y;
					isMoved = false;
					hasResLongEvent = false;
					postDelayed(mLongPressRunnable,
							ViewConfiguration.getLongPressTimeout());
					break;

				case MotionEvent.ACTION_MOVE:
					if (mode == DRAG) {
						float deltaX = curr.x - last.x;
						float deltaY = curr.y - last.y;
						float fixTransX = getFixDragTrans(deltaX, viewWidth,
								origWidth * saveScale);
						float fixTransY = getFixDragTrans(deltaY, viewHeight,
								origHeight * saveScale);
						matrix.postTranslate(fixTransX, fixTransY);

						if (saveScale > oldScale) {
							matrix.getValues(m);
							float transX = m[Matrix.MTRANS_X];
							if (transX > 0) {
								atLifeSide = true;
							} else {
								atLifeSide = false;
							}

							if (transX < (viewWidth - origWidth * saveScale)) {
								atRightSide = true;
							} else {
								atRightSide = false;
							}

							float transY = m[Matrix.MTRANS_Y];
							if (transY > 0) {
								atTopSide = true;
							} else {
								atTopSide = false;
							}

							if (transY < (viewHeight - origHeight * saveScale)) {
								atBottomSide = true;
							} else {
								atBottomSide = false;
							}
						} else {
							atLifeSide = atRightSide = atTopSide = atBottomSide = false;
						}

						fixTrans();
						last.set(curr.x, curr.y);
					}

					// cancel long click
					if (isMoved)
						break;
					if (Math.abs(mLastMotionX - curr.x) > TOUCH_SLOP
							|| Math.abs(mLastMotionY - curr.y) > TOUCH_SLOP) {
						// 移动超过阈值，则表示移动了
						isMoved = true;
						hasResLongEvent = false;
						removeCallbacks(mLongPressRunnable);
					}
					break;

				case MotionEvent.ACTION_UP:
					mode = NONE;
					int xDiff = (int) Math.abs(curr.x - start.x);
					int yDiff = (int) Math.abs(curr.y - start.y);
					if (xDiff < CLICK && yDiff < CLICK) {
						if (hasResLongEvent == false) //
							performClick();
					}

					// cancel long click
					hasResLongEvent = false;
					removeCallbacks(mLongPressRunnable);
					break;

				case MotionEvent.ACTION_POINTER_UP:
					mode = NONE;
					break;

				case MotionEvent.ACTION_CANCEL:
					// cancel long click
					hasResLongEvent = false;
					removeCallbacks(mLongPressRunnable);
					break;
				}

				setImageMatrix(matrix);
				invalidate();
				return true; // indicate event was handled
			}

		});
	}

	public void setMaxZoom(float x) {
		maxScale = x;
	}

	private class ScaleListener extends
			ScaleGestureDetector.SimpleOnScaleGestureListener {
		@Override
		public boolean onScaleBegin(ScaleGestureDetector detector) {
			mode = ZOOM;
			return true;
		}

		@Override
		public boolean onScale(ScaleGestureDetector detector) {
			float mScaleFactor = detector.getScaleFactor();
			float origScale = saveScale;
			saveScale *= mScaleFactor;
			if (saveScale > maxScale) {
				isScale = false;
				saveScale = maxScale;
				mScaleFactor = maxScale / origScale;
			} else if (saveScale < minScale) {
				saveScale = minScale;
				isScale = true;
				mScaleFactor = minScale / origScale;
			}

			// if (origWidth * saveScale <= viewWidth
			// || origHeight * saveScale <= viewHeight) {
			// Log.e(TouchImageView.class.getName(), "222222222222222222 if");
			// matrix.postScale(mScaleFactor, mScaleFactor, viewWidth / 2,
			// viewHeight / 2);
			// } else {
			// Log.e(TouchImageView.class.getName(), "222222222222222222 else");
			// matrix.postScale(mScaleFactor, mScaleFactor,
			// detector.getFocusX(), detector.getFocusY());
			// }

			if (origWidth * saveScale <= viewWidth
					|| origHeight * saveScale <= viewHeight) {
				Log.e(TouchImageView.class.getName(),
						"222222222222222222 if viewWidth=" + viewWidth
								+ "--viewHeight=" + viewHeight);
				matrix.postScale(mScaleFactor, mScaleFactor, viewWidth / 2,
						viewHeight / 2);
				fixTransCenter();
			} else {
				Log.e(TouchImageView.class.getName(),
						"222222222222222222 else viewWidth=" + viewWidth
								+ "--viewHeight=" + viewHeight);
				matrix.postScale(mScaleFactor, mScaleFactor,
						detector.getFocusX(), detector.getFocusY());
				fixTrans();
			}
			// Log.e(TouchImageView.class.getName(),
			// "mScaleFactor="+mScaleFactor+"--");
			return true;
		}
	}

	void fixTransCenter() {
		matrix.getValues(m);
		float transX = m[Matrix.MTRANS_X];
		float transY = m[Matrix.MTRANS_Y];
		float fixTransX = (viewWidth - origWidth * saveScale) / 2 - transX;
		float fixTransY = (viewHeight - origHeight * saveScale) / 2 - transY;
		Log.e(TouchImageView.class.getName(), "fixTransCenter transX=" + transX
				+ "--transY=" + transY + "--origWidth=" + origWidth
				+ "--origHeight=" + origHeight + "--saveScale=" + saveScale);
		Log.e(TouchImageView.class.getName(), "fixTransCenter fixTransX="
				+ fixTransX + "--fixTransY=" + fixTransY);

		if (fixTransX != 0 || fixTransY != 0)
			matrix.postTranslate(fixTransX, fixTransY);
	}

	void fixTrans() {
		matrix.getValues(m);
		float transX = m[Matrix.MTRANS_X];
		float transY = m[Matrix.MTRANS_Y];
		float fixTransX = getFixTrans(transX, viewWidth, origWidth * saveScale);
		float fixTransY = getFixTrans(transY, viewHeight, origHeight
				* saveScale);
		Log.e(TouchImageView.class.getName(), "transX=" + transX + "--transY="
				+ transY + "--origWidth=" + origWidth + "--origHeight="
				+ origHeight + "--saveScale=" + saveScale);
		Log.e(TouchImageView.class.getName(), "fixTransX=" + fixTransX
				+ "--fixTransY=" + fixTransY);
		if (fixTransX != 0 || fixTransY != 0)
			matrix.postTranslate(fixTransX, fixTransY);
	}

	float getFixTrans(float trans, float viewSize, float contentSize) {
		float minTrans, maxTrans;

		if (contentSize <= viewSize) {
			minTrans = 0;
			maxTrans = viewSize - contentSize;
		} else {
			minTrans = viewSize - contentSize;
			maxTrans = 0;
		}

		if (trans < minTrans)
			return -trans + minTrans;
		if (trans > maxTrans)
			return -trans + maxTrans;
		return 0;
	}

	void fixTransToCenter() {
		float fixTransX = getFixTransCenter(viewWidth, origWidth * saveScale);
		float fixTransY = getFixTransCenter(viewHeight, origHeight * saveScale);
		Log.e(TouchImageView.class.getName(), "fixTransX=" + fixTransX
				+ "--fixTransY=" + fixTransY);
		if (fixTransX != 0 || fixTransY != 0)
			matrix.postTranslate(fixTransX / 2, fixTransY / 2);
	}

	float getFixTransCenter(float viewSize, float contentSize) {
		if (contentSize <= viewSize) {
			return (viewSize - contentSize) / 2;
		} else {
			return (viewSize - contentSize) / 2;
		}
	}

	float getFixDragTrans(float delta, float viewSize, float contentSize) {
		if (contentSize <= viewSize) {
			return 0;
		}
		return delta;
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		viewWidth = MeasureSpec.getSize(widthMeasureSpec);
		viewHeight = MeasureSpec.getSize(heightMeasureSpec);
		if (oldMeasuredHeight == viewWidth && oldMeasuredHeight == viewHeight
				|| viewWidth == 0 || viewHeight == 0)
			return;
		oldMeasuredHeight = viewHeight;
		oldMeasuredWidth = viewWidth;
		if (saveScale == 1) {
			fitToScreen();
		}
		fixTrans();
	}

	private void fitToScreen() {
		float scale;
		Drawable drawable = getDrawable();
		if (drawable == null || drawable.getIntrinsicWidth() == 0
				|| drawable.getIntrinsicHeight() == 0)
			return;
		int bmWidth = drawable.getIntrinsicWidth();
		int bmHeight = drawable.getIntrinsicHeight();

		// Print.d("bmSize", "bmWidth: " + bmWidth + " bmHeight : " + bmHeight);

		float scaleX = (float) viewWidth / (float) bmWidth;
		float scaleY = (float) viewHeight / (float) bmHeight;
		scale = Math.min(scaleX, scaleY);
		oldScale = scale;
		// saveScale = 1f*oldScale;
		matrix.setScale(scale, scale);

		// Center the image
		float redundantYSpace = (float) viewHeight - (scale * (float) bmHeight);
		float redundantXSpace = (float) viewWidth - (scale * (float) bmWidth);
		redundantYSpace /= (float) 2;
		redundantXSpace /= (float) 2;

		matrix.postTranslate(redundantXSpace, redundantYSpace);

		origWidth = viewWidth - 2 * redundantXSpace;
		origHeight = viewHeight - 2 * redundantYSpace;
		setImageMatrix(matrix);
		Log.e(TouchImageView.class.getName(), "oldScale=" + oldScale);
	}

	@Override
	public boolean onDown(MotionEvent e) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public void onShowPress(MotionEvent e) {
		// TODO Auto-generated method stub

	}

	@Override
	public boolean onSingleTapUp(MotionEvent e) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
			float distanceY) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public void onLongPress(MotionEvent e) {
		// TODO Auto-generated method stub

	}

	@Override
	public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
			float velocityY) {
		// TODO Auto-generated method stub
		return false;
	}
}